package classifiers;

import draw.ExploringMedics;
import draw.ImageFromArray;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

public class CircleSquareAction extends AbstractDoAction implements IsRepaint {

    int whiteRegions[][];
    int blackRegions[][];
    int regionSize = 0;
    int smallSize = 0;
    public int minx = Integer.MAX_VALUE;
    public int miny = Integer.MAX_VALUE;
    public int maxx = 0;
    public int maxy = 0;

    public CircleSquareAction(int[][] regionsOfInterest) {
        super(regionsOfInterest);
        whiteRegions = new int[width][height];
        blackRegions = new int[width][height];
        initMask();
    }
    private final int UNEXPLORED_FOR_MASK = -1;
    private final int EDGE_BLACK = -2;
    private final int EDGE_WHITE = -3;
    private final int lineThinnes = 5;

    private void initMask() {
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                whiteRegions[i][j] = regionsOfInterest[i][j] == ExploringMedics.WHITE
                        ? UNEXPLORED_FOR_MASK : EDGE_BLACK;
                blackRegions[i][j] = regionsOfInterest[i][j] == ExploringMedics.BLACK
                        ? UNEXPLORED_FOR_MASK : EDGE_WHITE;
            }
        }
        int mark = 1;
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (whiteRegions[i][j] == UNEXPLORED_FOR_MASK) {
                    regionSize = 0;
                    minx = Integer.MAX_VALUE;
                    miny = Integer.MAX_VALUE;
                    maxx = 0;
                    maxy = 0;
                    markConnectedComponent(i, j, mark, whiteRegions);

                    if (regionSize <= smallSize) {
//                        System.out.print(regionSize + "  ");
//                        System.out.print(regionsOfInterest[i][j]+ "  ");
                        repaintSmallRegion(mark, ExploringMedics.BLACK, whiteRegions);
//                        System.out.println(regionsOfInterest[i][j]);
                    }
                    mark++;
                }
            }
        }

        mark = 1;
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (blackRegions[i][j] == UNEXPLORED_FOR_MASK) {
                    regionSize = 0;
                    minx = Integer.MAX_VALUE;
                    miny = Integer.MAX_VALUE;
                    maxx = 0;
                    maxy = 0;
                    markConnectedComponent(i, j, mark, blackRegions);
                    if (regionSize <= smallSize) {
                        repaintSmallRegion(mark, ExploringMedics.WHITE, blackRegions);
                    }
                    mark++;
                }
            }
        }

    }

    void repaintSmallRegion(int mark, int color, int array[][]) {
        for (int i = minx; i <= maxx; i++) {
            for (int j = miny; j <= maxy; j++) {
                if (array[i][j] == mark) {
                    regionsOfInterest[i][j] = color;
                }
            }
        }
    }

    void markConnectedComponent(int xPoint, int yPoint, int mark, int mas[][]) {
        LinkedList<Integer> xQueue = new LinkedList<Integer>();
        LinkedList<Integer> yQueue = new LinkedList<Integer>();

        xQueue.push(xPoint);
        yQueue.push(yPoint);

        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};

        while (!xQueue.isEmpty()) {
            int x = xQueue.pop();
            int y = yQueue.pop();

            if (x >= width || y >= height || x < 0 || y < 0) {
                continue;
            }

            if (mas[x][y] == UNEXPLORED_FOR_MASK) {
                mas[x][y] = mark;

                regionSize++;

                minx = x < minx ? x : minx;
                miny = y < miny ? y : miny;
                maxx = x > maxx ? x : maxx;
                maxy = y > maxy ? y : maxy;

                for (int i = 0; i < 4; i++) {
                    xQueue.push(x + dx[i]);
                    yQueue.push(y + dy[i]);
                }
            }
        }
    }

    private void expandRegion(int xPoint, int yPoint, int mas[][], int EDGE_COLOR) {

        int curMark = mas[xPoint][yPoint];

        LinkedList<Integer> xQueue = new LinkedList<Integer>();
        LinkedList<Integer> yQueue = new LinkedList<Integer>();

        xQueue.push(xPoint);
        yQueue.push(yPoint);

        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};

        while (!xQueue.isEmpty()) {
            int x = xQueue.pop();
            int y = yQueue.pop();

            if (x >= width || y >= height || x < 0 || y < 0) {
                continue;
            }

            minx = x < minx ? x : minx;
            miny = y < miny ? y : miny;
            maxx = x > maxx ? x : maxx;
            maxy = y > maxy ? y : maxy;

            if (mas[x][y] == curMark || mas[x][y] == EDGE_COLOR) {
                if (mas[x][y] == EDGE_COLOR && !canRepaintBlack(x, y, curMark, mas, EDGE_COLOR)) {
                    continue;
                }

                mas[x][y] = UNEXPLORED_FOR_MASK;

                for (int i = 0; i < 4; i++) {
                    xQueue.push(x + dx[i]);
                    yQueue.push(y + dy[i]);
                }
            }
        }
        for (int i = minx; i <= maxx; i++) {
            for (int j = miny; j <= maxy; j++) {
                mas[i][j] = mas[i][j] == UNEXPLORED_FOR_MASK ? curMark : mas[i][j];
            }
        }
    }

    boolean canRepaintBlack(int x, int y, int curMask, int mas[][], int EDGE_COLOR) {
        int sDiv2 = lineThinnes;
        if (x <= sDiv2 - 1 || y <= sDiv2 - 1 || x >= width - sDiv2 || y >= height - sDiv2) {
            return false;
        }
        for (int i = x - sDiv2; i <= x + sDiv2; i++) {
            for (int j = y - sDiv2; j <= y + sDiv2; j++) {
                if (mas[i][j] != UNEXPLORED_FOR_MASK
                        && mas[i][j] != curMask && mas[i][j] != EDGE_COLOR) {
                    return false;
                }
            }
        }
        return true;
    }

    private void recalcRegions(int mas[][], int mark) {
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (mas[i][j] == mark) {
                    regionsOfInterest[i][j] = ExploringMedics.WHITE;
                }
            }
        }
    }

    @Override
    public void doAction(int xStart, int yStart) {
        int mark = 0;
        if (regionsOfInterest[xStart][yStart] == ExploringMedics.WHITE) {
            mark = whiteRegions[xStart][yStart];
            expandRegion(xStart, yStart, whiteRegions, EDGE_BLACK);
            recalcRegions(whiteRegions, mark);
        } else {
            mark = blackRegions[xStart][yStart];
            expandRegion(xStart, yStart, blackRegions, EDGE_WHITE);
            recalcRegions(blackRegions, mark);
        }

    }

    @Override
    public BufferedImage getImage() {
        BufferedImage image = ImageFromArray.createImageFromArray(regionsOfInterest);
        return image;
    }
}
